{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "___\n",
    "\n",
    "<a href='https://www.udemy.com/user/joseportilla/'><img src='../Pierian_Data_Logo.png'/></a>\n",
    "___\n",
    "<center><em>Content Copyright by Pierian Data</em></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Nested Statements and Scope \n",
    "\n",
    "Now that we have gone over writing our own functions, it's important to understand how Python deals with the variable names you assign. When you create a variable name in Python the name is stored in a *name-space*. Variable names also have a *scope*, the scope determines the visibility of that variable name to other parts of your code.\n",
    "\n",
    "Let's start with a quick thought experiment; imagine the following code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "x = 25\n",
    "\n",
    "def printer():\n",
    "    x = 50\n",
    "    return x\n",
    "\n",
    "# print(x)\n",
    "# print(printer())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What do you imagine the output of printer() is? 25 or 50? What is the output of print x? 25 or 50?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25\n"
     ]
    }
   ],
   "source": [
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "50\n"
     ]
    }
   ],
   "source": [
    "print(printer())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Interesting! But how does Python know which **x** you're referring to in your code? This is where the idea of scope comes in. Python has a set of rules it follows to decide what variables (such as **x** in this case) you are referencing in your code. Lets break down the rules:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "This idea of scope in your code is very important to understand in order to properly assign and call variable names. \n",
    "\n",
    "In simple terms, the idea of scope can be described by 3 general rules:\n",
    "\n",
    "1. Name assignments will create or change local names by default.\n",
    "2. Name references search (at most) four scopes, these are:\n",
    "    * local\n",
    "    * enclosing functions\n",
    "    * global\n",
    "    * built-in\n",
    "3. Names declared in global and nonlocal statements map assigned names to enclosing module and function scopes.\n",
    "\n",
    "\n",
    "The statement in #2 above can be defined by the LEGB rule.\n",
    "\n",
    "**LEGB Rule:**\n",
    "\n",
    "L: Local — Names assigned in any way within a function (def or lambda), and not declared global in that function.\n",
    "\n",
    "E: Enclosing function locals — Names in the local scope of any and all enclosing functions (def or lambda), from inner to outer.\n",
    "\n",
    "G: Global (module) — Names assigned at the top-level of a module file, or declared global in a def within the file.\n",
    "\n",
    "B: Built-in (Python) — Names preassigned in the built-in names module : open, range, SyntaxError,..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quick examples of LEGB\n",
    "\n",
    "### Local"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# x is local here:\n",
    "f = lambda x:x**2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Enclosing function locals\n",
    "This occurs when we have a function inside a function (nested functions)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello Sammy\n"
     ]
    }
   ],
   "source": [
    "name = 'This is a global name'\n",
    "\n",
    "def greet():\n",
    "    # Enclosing function\n",
    "    name = 'Sammy'\n",
    "    \n",
    "    def hello():\n",
    "        print('Hello '+name)\n",
    "    \n",
    "    hello()\n",
    "\n",
    "greet()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note how Sammy was used, because the hello() function was enclosed inside of the greet function!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Global\n",
    "Luckily in Jupyter a quick way to test for global variables is to see if another cell recognizes the variable!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "This is a global name\n"
     ]
    }
   ],
   "source": [
    "print(name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Built-in\n",
    "These are the built-in function names in Python (don't overwrite these!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function len>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Local Variables\n",
    "When you declare variables inside a function definition, they are not related in any way to other variables with the same names used outside the function - i.e. variable names are local to the function. This is called the scope of the variable. All variables have the scope of the block they are declared in starting from the point of definition of the name.\n",
    "\n",
    "Example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x is 50\n",
      "Changed local x to 2\n",
      "x is still 50\n"
     ]
    }
   ],
   "source": [
    "x = 50\n",
    "\n",
    "def func(x):\n",
    "    print('x is', x)\n",
    "    x = 2\n",
    "    print('Changed local x to', x)\n",
    "\n",
    "func(x)\n",
    "print('x is still', x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first time that we print the value of the name **x** with the first line in the function’s body, Python uses the value of the parameter declared in the main block, above the function definition.\n",
    "\n",
    "Next, we assign the value 2 to **x**. The name **x** is local to our function. So, when we change the value of **x** in the function, the **x** defined in the main block remains unaffected.\n",
    "\n",
    "With the last print statement, we display the value of **x** as defined in the main block, thereby confirming that it is actually unaffected by the local assignment within the previously called function.\n",
    "\n",
    "## The <code>global</code> statement\n",
    "If you want to assign a value to a name defined at the top level of the program (i.e. not inside any kind of scope such as functions or classes), then you have to tell Python that the name is not local, but it is global. We do this using the <code>global</code> statement. It is impossible to assign a value to a variable defined outside a function without the global statement.\n",
    "\n",
    "You can use the values of such variables defined outside the function (assuming there is no variable with the same name within the function). However, this is not encouraged and should be avoided since it becomes unclear to the reader of the program as to where that variable’s definition is. Using the <code>global</code> statement makes it amply clear that the variable is defined in an outermost block.\n",
    "\n",
    "Example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Before calling func(), x is:  50\n",
      "This function is now using the global x!\n",
      "Because of global x is:  50\n",
      "Ran func(), changed global x to 2\n",
      "Value of x (outside of func()) is:  2\n"
     ]
    }
   ],
   "source": [
    "x = 50\n",
    "\n",
    "def func():\n",
    "    global x\n",
    "    print('This function is now using the global x!')\n",
    "    print('Because of global x is: ', x)\n",
    "    x = 2\n",
    "    print('Ran func(), changed global x to', x)\n",
    "\n",
    "print('Before calling func(), x is: ', x)\n",
    "func()\n",
    "print('Value of x (outside of func()) is: ', x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "The <code>global</code> statement is used to declare that **x** is a global variable - hence, when we assign a value to **x** inside the function, that change is reflected when we use the value of **x** in the main block.\n",
    "\n",
    "You can specify more than one global variable using the same global statement e.g. <code>global x, y, z</code>."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Conclusion\n",
    "You should now have a good understanding of Scope (you may have already intuitively felt right about Scope which is great!) One last mention is that you can use the **globals()** and **locals()** functions to check what are your current local and global variables.\n",
    "\n",
    "Another thing to keep in mind is that everything in Python is an object! I can assign variables to functions just like I can with numbers! We will go over this again in the decorator section of the course!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
